﻿namespace DomainServices.Tools
{
    using DomainServices.Tools.SharedTypes;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.ServiceModel.DomainServices;
    using System.Text;
    using System.Web.Hosting;
    using System.ServiceModel.DomainServices.Server;

    internal class ClientCodeGenerationDispatcher : MarshalByRefObject, IRegisteredObject, IDisposable
    {
        private CompositionContainer _compositionContainer;
        private ComposablePartCatalog _partCatalog;

        private void CreateCompositionContainer(IEnumerable<string> compositionAssemblyPaths, ILogger logger)
        {
            try
            {
                IEnumerable<AssemblyCatalog> catalogs = from a in GetCompositionAssemblies(compositionAssemblyPaths, logger) select new AssemblyCatalog(a);
                this._partCatalog = new AggregateCatalog(catalogs);
                this._compositionContainer = new CompositionContainer(this._partCatalog, new ExportProvider[0]);
                this._compositionContainer.ComposeParts(new object[] { this });
            }
            catch (Exception exception)
            {
                logger.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.Failed_To_Create_Composition_Container, new object[] { exception.Message }));
                this._partCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
                this._compositionContainer = new CompositionContainer(this._partCatalog, new ExportProvider[0]);
                this._compositionContainer.ComposeParts(new object[] { this });
            }
        }

        public void Dispose()
        {
            CompositionContainer container = this._compositionContainer;
            ComposablePartCatalog catalog = this._partCatalog;
            this._compositionContainer = null;
            this._partCatalog = null;
            if (container != null)
            {
                container.Dispose();
            }
            if (catalog != null)
            {
                catalog.Dispose();
            }
        }

        internal IDomainServiceClientCodeGenerator FindCodeGenerator(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable<string> compositionAssemblies, string codeGeneratorName)
        {
            System.Func<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>, bool> func = null;
            System.Func<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>, bool> func2 = null;
            if (string.IsNullOrEmpty(options.Language))
            {
                throw new ArgumentException(Resource.Null_Language_Property, "options");
            }
            IDomainServiceClientCodeGenerator generator = null;
            if (!string.IsNullOrEmpty(codeGeneratorName) && codeGeneratorName.Contains<char>(','))
            {
                Type c = Type.GetType(codeGeneratorName, false);
                if (c != null)
                {
                    if (!typeof(IDomainServiceClientCodeGenerator).IsAssignableFrom(c))
                    {
                        host.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.Code_Generator_Incorrect_Type, new object[] { codeGeneratorName }));
                    }
                    else
                    {
                        try
                        {
                            generator = Activator.CreateInstance(c) as IDomainServiceClientCodeGenerator;
                        }
                        catch (Exception exception)
                        {
                            if (exception.IsFatal())
                            {
                                throw;
                            }
                            host.LogError(string.Format(CultureInfo.CurrentCulture, Resource.Code_Generator_Instantiation_Error, new object[] { codeGeneratorName, exception.Message }));
                        }
                    }
                }
            }
            if (generator != null)
            {
                return generator;
            }
            this.CreateCompositionContainer(compositionAssemblies, host);
            if ((this.DomainServiceClientCodeGenerators == null) || !this.DomainServiceClientCodeGenerators.Any<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>())
            {
                return generator;
            }
            if (func == null)
            {
                func = i => string.Equals(options.Language, i.Metadata.Language, StringComparison.OrdinalIgnoreCase);
            }
            IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> source = Enumerable.Where<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>(this.DomainServiceClientCodeGenerators, func);
            Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> lazy = null;
            if (!string.IsNullOrEmpty(codeGeneratorName))
            {
                if (func2 == null)
                {
                    func2 = i => string.Equals(i.Metadata.GeneratorName, codeGeneratorName, StringComparison.OrdinalIgnoreCase);
                }
                IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> enumerable2 = Enumerable.Where<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>(source, func2);
                switch (enumerable2.Count<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>())
                {
                    case 0:
                        host.LogError(string.Format(CultureInfo.CurrentCulture, Resource.Code_Generator_Not_Found, new object[] { codeGeneratorName, options.Language, options.ServerProjectPath, options.ClientProjectPath, "DomainServices.Tools.CodeDomClientCodeGenerator" }));
                        goto Label_049C;

                    case 1:
                        lazy = enumerable2.First<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>();
                        goto Label_049C;
                }
                StringBuilder builder = new StringBuilder();
                foreach (Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> lazy2 in from i in enumerable2
                    orderby i.Value.GetType().FullName
                    select i)
                {
                    builder.AppendLine("    " + lazy2.Value.GetType().FullName);
                }
                host.LogError(string.Format(CultureInfo.CurrentCulture, Resource.Multiple_Named_Code_Generators, new object[] { codeGeneratorName, options.Language, builder.ToString(), options.ServerProjectPath, options.ClientProjectPath, enumerable2.First<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>().Value.GetType().AssemblyQualifiedName }));
            }
            else if (source.Count<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>() == 1)
            {
                lazy = source.First<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>();
            }
            else
            {
                IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> enumerable3 = from i in source
                    where !string.Equals("DomainServices.Tools.CodeDomClientCodeGenerator", i.Metadata.GeneratorName, StringComparison.OrdinalIgnoreCase)
                    select i;
                switch (enumerable3.Count<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>())
                {
                    case 1:
                        lazy = enumerable3.First<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>();
                        host.LogMessage(string.Format(CultureInfo.CurrentCulture, Resource.Using_Custom_Code_Generator, new object[] { lazy.Metadata.GeneratorName }));
                        goto Label_049C;

                    case 0:
                        goto Label_049C;
                }
                StringBuilder builder2 = new StringBuilder();
                IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> enumerable4 = from i in enumerable3
                    orderby i.Metadata.GeneratorName
                    select i;
                foreach (Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata> lazy3 in enumerable4)
                {
                    builder2.AppendLine("    " + lazy3.Metadata.GeneratorName);
                }
                host.LogWarning(string.Format(CultureInfo.CurrentCulture, Resource.Multiple_Custom_Code_Generators_Using_Default, new object[] { options.Language, builder2.ToString(), options.ClientProjectPath, enumerable4.First<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>().Metadata.GeneratorName, "DomainServices.Tools.CodeDomClientCodeGenerator" }));
                lazy = Enumerable.FirstOrDefault<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>>(source, i => string.Equals("DomainServices.Tools.CodeDomClientCodeGenerator", i.Metadata.GeneratorName, StringComparison.OrdinalIgnoreCase));
            }
        Label_049C:
            return ((lazy == null) ? null : lazy.Value);
        }

        internal string GenerateCode(ClientCodeGenerationOptions options, SharedCodeServiceParameters parameters, ILoggingService loggingService, string codeGeneratorName)
        {
            SilverlightAppDomainUtilities.ConfigureAppDomain(options.ClientFrameworkPath);
            using (SharedCodeService service = new SharedCodeService(parameters, loggingService))
            {
                CodeGenerationHost host = new CodeGenerationHost(loggingService, service);
                return this.GenerateCode(host, options, parameters.ServerAssemblies, codeGeneratorName);
            }
        }

        internal string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable<string> assembliesToLoad, string codeGeneratorName)
        {
            ILogger logger = host;
            DomainServiceCatalog catalog = new DomainServiceCatalog(assembliesToLoad, logger);
            return this.GenerateCode(host, options, catalog, assembliesToLoad, codeGeneratorName);
        }

        internal string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, IEnumerable<Type> domainServiceTypes, IEnumerable<string> compositionAssemblies, string codeGeneratorName)
        {
            ILogger logger = host;
            DomainServiceCatalog catalog = new DomainServiceCatalog(domainServiceTypes, logger);
            return this.GenerateCode(host, options, catalog, compositionAssemblies, codeGeneratorName);
        }

        private string GenerateCode(ICodeGenerationHost host, ClientCodeGenerationOptions options, DomainServiceCatalog catalog, IEnumerable<string> compositionAssemblies, string codeGeneratorName)
        {
            IEnumerable<DomainServiceDescription> domainServiceDescriptions = catalog.DomainServiceDescriptions;
            IDomainServiceClientCodeGenerator generator = this.FindCodeGenerator(host, options, compositionAssemblies, codeGeneratorName);
            string str = null;
            if (generator != null)
            {
                try
                {
                    str = generator.GenerateCode(host, domainServiceDescriptions, options);
                }
                catch (Exception exception)
                {
                    if (exception.IsFatal())
                    {
                        throw;
                    }
                    host.LogError(string.Format(CultureInfo.CurrentCulture, Resource.CodeGenerator_Threw_Exception, new object[] { string.IsNullOrEmpty(codeGeneratorName) ? generator.GetType().FullName : codeGeneratorName, options.ClientProjectPath, exception.Message }));
                }
            }
            return str;
        }

        private static IEnumerable<Assembly> GetCompositionAssemblies(IEnumerable<string> compositionAssemblyPaths, ILogger logger)
        {
            HashSet<Assembly> set = new HashSet<Assembly>();
            if (compositionAssemblyPaths != null)
            {
                foreach (string str in compositionAssemblyPaths)
                {
                    Assembly assembly = AssemblyUtilities.LoadAssembly(str, logger);
                    if ((assembly != null) && !assembly.IsSystemAssembly())
                    {
                        set.Add(assembly);
                    }
                }
            }
            set.Add(typeof(ClientCodeGenerationDispatcher).Assembly);
            return set;
        }

        void IRegisteredObject.Stop(bool immediate)
        {
        }

        [ImportMany(typeof(IDomainServiceClientCodeGenerator))]
        internal IEnumerable<Lazy<IDomainServiceClientCodeGenerator, ICodeGeneratorMetadata>> DomainServiceClientCodeGenerators { get; set; }
    }
}

